﻿
using System;
using System.Collections.Generic;
using System.IO;

namespace MX.Core.Caching.Redis.Sider.Executors
{
	internal class TransactedExecutor : PipelinedExecutor
	{
		Stream runStream = null;

		public Stream Stream {
			get{ return runStream; }
			 
		}

		public TransactedExecutor (Stream stream) : base (stream)
		{
			runStream = stream;
		}

		public override Mode Mode { get { return Mode.MultiExec; } }

		public override void Init (IExecutor previous)
		{
			if (previous is TransactedExecutor)
				throw new InvalidOperationException ("Cannot nest MULTI/EXEC.");

			// TODO: Do a really thorough test that this won't randomly break\
			// TODO: Maybe shouldn't really auto-pipeline by default because
			//   some commands could throw an error and it would be hard to debug
			//   if the error only happens when .Finish() (EXEC) is called.
			//   This shouldn't be the case with manual piplining because the scope
			//   is explicitly stated by the user.
			if (previous is PipelinedExecutor)
				throw new InvalidOperationException (
					"MULTI/EXEC is automatically pipelined.");

			base.Init (previous);
		}


		public override T Execute<T> (Invocation<T>  invocation, Stream stream)
		{
			// TODO: Provide a settings to pipeline MultiExec automatically
			return base.Execute (invocation, stream); // issue write and queue reads
		}

		public override IEnumerable<T> Finish<T> ()
		{
			Writer.WriteCmdStart (runStream, "EXEC", 0);
			Writer.Flush (runStream);

			// reads out an equal number of +QUEUEDs beforehand
			// TODO: May need to restart if a read doesn't return an expected +QUEUEDs
			//   which means *some* commands may have been improperly sent
			//   may need to issue a DISCARD and start over.
			for (int i = 0; i < ReadsQueue.Count; i++)
				Reader.ReadQueued (runStream);

			Reader.ReadMultiBulkHeader (runStream); // read-out a multi-bulk header
			return base.Finish<T> (); // read out all the queued reads
		}

		public void Discard ()
		{
			ReadsQueue.Clear ();
		}
	}
}
